package com.tianzmp.ttq.admin.util;

import com.google.common.collect.Lists;
import lombok.extern.log4j.Log4j2;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import org.apache.commons.collections4.MapUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created on 2021/4/7.
 *
 * @author zhump
 */
public class MapperUtils {
    /**
     * 默认字段工厂
     */
    private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();

    /**
     * 默认字段实例
     */
    private static final MapperFacade MAPPER_FACADE = MAPPER_FACTORY.getMapperFacade();

    /**
     * 默认字段实例集合
     */
    private static final Map<String, MapperFacade> CACHE_MAPPER_FACADE_MAP = new ConcurrentHashMap<>();

    /**
     * 映射实体（默认字段）
     *
     * @param toClass 映射类对象
     * @param data    数据（对象）
     * @return 映射类对象
     */
    public static <E, T> E map(Class<E> toClass, T data) {
        return MAPPER_FACADE.map(data, toClass);
    }

    /**
     * 字符串常量set
     */
    private static final String CONSTANT_SET = "set";

    /**
     * 字符串常量
     */
    private static final String CONSTANT_DOT = ".";
    /**
     * 切割字符串
     */
    private static final String CONSTANT_REGEX_DOT = "\\.";

    private static final String CONSTANT_TYPE_LIST = "List";

    private static final String CONSTANT_TYPE_SET = "set";

    /**
     * 映射实体（自定义配置）
     *
     * @param toClass   映射类对象
     * @param data      数据（对象）
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    public static <E, T> E map(Class<E> toClass, T data, Map<String, String> configMap) {
        Map<String, String> var1 = wipeOff(configMap);
        MapperFacade mapperFacade = getMapperFacade(toClass, data.getClass(), configMap);
        E e = mapperFacade.map(data, toClass);
        if (MapUtils.isEmpty(var1)) {
            return e;
        }
        return setChildNode(data, e, var1);
    }

    /**
     * 映射集合（默认字段）
     *
     * @param toClass 映射类对象
     * @param data    数据（集合）
     * @return 映射类对象
     */
    public static <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data) {
        if (CollectionUtils.isEmpty(data)) {
            return Lists.newArrayList();
        }
        return MAPPER_FACADE.mapAsList(data, toClass);
    }

    /**
     * 映射集合（自定义配置）
     *
     * @param toClass   映射类
     * @param data      数据（集合）
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    public static <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data, Map<String, String> configMap) {
        if (CollectionUtils.isEmpty(data)) {
            return Lists.newArrayList();
        }
        Map<String, String> var1 = wipeOff(configMap);
        T t = data.stream().findFirst().get();
        MapperFacade mapperFacade = getMapperFacade(toClass, t.getClass(), configMap);
        List<E> e = mapperFacade.mapAsList(data, toClass);
        if (MapUtils.isEmpty(var1)) {
            return e;
        }
        return setChildNode(data, e, var1);
    }

    /**
     * 映射集合（默认字段）
     *
     * @param toClass 映射类对象
     * @param data    数据（集合）
     * @return 映射类对象
     */
    public static <E, T> Set<E> mapAsSet(Class<E> toClass, Collection<T> data) {
        return MAPPER_FACADE.mapAsSet(data, toClass);
    }

    /**
     * 映射集合（自定义配置）
     *
     * @param toClass   映射类
     * @param data      数据（集合）
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    public static <E, T> Set<E> mapAsSet(Class<E> toClass, Collection<T> data, Map<String, String> configMap) {
        if (CollectionUtils.isEmpty(data)) {
            return null;
        }
        Map<String, String> var1 = wipeOff(configMap);
        T t = data.stream().findFirst().get();
        MapperFacade mapperFacade = getMapperFacade(toClass, t.getClass(), configMap);
        Set<E> e = mapperFacade.mapAsSet(data, toClass);
        if (MapUtils.isEmpty(var1)) {
            return e;
        }
        return setChildNode(data, e, var1);
    }

    private static <E, T> MapperFacade getMapperFacade(Class<E> toClass, Class<T> dataClass, Map<String, String> configMap) {
        String mapKey = dataClass.getCanonicalName() + "_" + toClass.getCanonicalName();
        MapperFacade mapperFacade = CACHE_MAPPER_FACADE_MAP.get(mapKey);
        if (Objects.isNull(mapperFacade)) {
            MapperFactory factory = new DefaultMapperFactory.Builder().build();
            ClassMapBuilder classMapBuilder = factory.classMap(dataClass, toClass);
            configMap.forEach(classMapBuilder::field);
            classMapBuilder.byDefault().register();
            mapperFacade = factory.getMapperFacade();
            CACHE_MAPPER_FACADE_MAP.put(mapKey, mapperFacade);
        }
        return mapperFacade;
    }

    /**
     * 子节点赋值
     *
     * @param data 源数据
     * @param var1 需要替换的目标
     * @param e    目标数据
     * @param <E>  目标类型
     * @param <T>  源数据类型
     * @return 结果集
     */
    private static <E, T> E setChildNode(T data, E e, Map<String, String> var1) {
        Map<String, String> var2 = demotion(var1);
        if (MapUtils.isEmpty(var2)) {
            return e;
        }
        Map<String, Field> sourceMapping = mappingField(data.getClass().getDeclaredFields());
        Map<String, Field> targetMapping = mappingField(e.getClass().getDeclaredFields());
        Iterator<Map.Entry<String, String>> iter = var2.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            Map<String, String> var3 = getFiltrate(var1, entry.getKey());
            Field source = sourceMapping.get(entry.getKey());
            Field target = targetMapping.get(entry.getValue());
            source.setAccessible(true);
            target.setAccessible(true);
            try {
                Method method = e.getClass().getMethod(getMethodName(entry.getValue()), target.getType());
                method.invoke(e, assignment(getRealClass(target), source.get(data), var3, distinctionType(target.getType())));
            } catch (IllegalAccessException illegalAccessException) {
                illegalAccessException.printStackTrace();
            } catch (InvocationTargetException invocationTargetException) {
                invocationTargetException.printStackTrace();
            } catch (NoSuchMethodException noSuchMethodException) {
                noSuchMethodException.printStackTrace();
            }
        }
        return e;
    }

    /**
     * 获取集合中泛型
     *
     * @param field 入参值
     * @return 结果集
     */
    private static Class getRealClass(Field field) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) field.getGenericType();
            return (Class) pt.getActualTypeArguments()[0];
        }
        return field.getType();

    }

    /**
     * 数据转换
     *
     * @param clz  待转换的类型
     * @param data 数据
     * @param var1 待转换
     * @param type 类型
     * @param <E>  目标
     * @param <T>  源
     * @return 结果集
     */
    private static <E, T> Object assignment(Class<E> clz, Object data, Map<String, String> var1, String type) {
        Object o;
        switch (type) {
            case CONSTANT_TYPE_LIST:
                List<T> list = (List<T>) data;
                o = mapAsList(clz, list, var1);
                break;
            case CONSTANT_TYPE_SET:
                Set<T> set = (Set<T>) data;
                o = mapAsSet(clz, set, var1);
                break;
            default:
                o = map(clz, data, var1);
        }
        return o;
    }

    /**
     * 确认对象类型
     *
     * @param clz type
     * @return 结果集
     */
    private static String distinctionType(Class clz) {
        try {
            if (List.class.isAssignableFrom(clz) || clz.newInstance() instanceof List) {
                return CONSTANT_TYPE_LIST;
            }
            if (Set.class.isAssignableFrom(clz) || clz.newInstance() instanceof Set) {
                return CONSTANT_TYPE_LIST;
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return "";
    }


    /**
     * 筛选
     *
     * @param var1
     * @return
     */
    private static Map<String, String> getFiltrate(Map<String, String> var1, String key) {
        Map<String, String> var2 = new HashMap<>(10);
        if (MapUtils.isEmpty(var1)) {
            return var2;
        }
        Iterator<Map.Entry<String, String>> iter = var1.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            if (entry.getKey().contains(key)) {
                var2.put(demotion(entry.getKey(), CONSTANT_DOT), demotion(entry.getValue(), CONSTANT_DOT));
            }
        }
        return var2;
    }

    /**
     * 截取字符串
     *
     * @param var1 截取目标
     * @param var2 待匹配的目标
     * @return 结果集
     */
    private static String demotion(String var1, String var2) {
        int index = var1.indexOf(var2) + 1;
        return var1.substring(index);
    }

    /**
     * 返回set的节点集
     *
     * @param fildeName 属性名称
     * @return 结果集
     */
    private static String getMethodName(String fildeName) {
        byte[] items = fildeName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(CONSTANT_SET).append(new String(items));
        return stringBuffer.toString();
    }

    /**
     * 节点名 映射Field
     *
     * @param fields
     * @return 映射结果
     */
    private static Map<String, Field> mappingField(Field[] fields) {
        Map<String, Field> var1 = new HashMap<>(10);
        for (Field field : fields) {
            var1.put(field.getName(), field);
        }
        return var1;
    }

    /**
     * 获取第一层节点
     *
     * @param var1 筛选目标
     * @return 结果集
     */
    private static Map<String, String> demotion(Map<String, String> var1) {
        Map<String, String> var2 = new HashMap<>(10);
        if (MapUtils.isEmpty(var1)) {
            return var2;
        }
        Iterator<Map.Entry<String, String>> iter = var1.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            String key = entry.getKey().split(CONSTANT_REGEX_DOT)[0];
            String value = entry.getValue().split(CONSTANT_REGEX_DOT)[0];
            var2.put(key, value);
        }
        return var2;
    }

    /**
     * 剔除有‘.’的key和value
     *
     * @param var1 待剔除的容器
     * @return 剔除元素的容器
     */
    private static Map<String, String> wipeOff(Map<String, String> var1) {
        Map<String, String> var2 = new HashMap<>(10);
        if (MapUtils.isEmpty(var1)) {
            return var2;
        }
        Iterator<Map.Entry<String, String>> iter = var1.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> entry = iter.next();
            String key = entry.getKey();
            if (key.contains(CONSTANT_DOT)) {
                var2.put(key, entry.getValue());
                iter.remove();
            }
        }
        return var2;
    }
}
